/**
 *    SPDX-License-Identifier: Apache-2.0
 */

const helper = require('../../common/helper');

const logger = helper.getLogger('MetricService');

const moment = require('moment')
/**
 *
 *
 * @class MetricService
 */
class MetricService {
	constructor(sql) {
		this.sql = sql;
	}

	/**
	 *
	 *
	 * @param {*} channel_genesis_hash
	 * @returns
	 * @memberof MetricService
	 */
	getChaincodeCount(network_name, channel_genesis_hash) {
		return this.sql.getRowsBySQlCase(
			`select count(1) c from chaincodes where channel_genesis_hash='${channel_genesis_hash}' and network_name='${network_name}' `
		);
	}

	/**
	 *
	 *
	 * @param {*} channel_genesis_hash
	 * @returns
	 * @memberof MetricService
	 */
	getPeerlistCount(network_name, channel_genesis_hash) {
		return this.sql.getRowsBySQlCase(
			`select count(1) c from peer where channel_genesis_hash='${channel_genesis_hash}' and peer_type='PEER' and network_name='${network_name}' `
		);
	}

	/**
	 *
	 *
	 * @param {*} channel_genesis_hash
	 * @returns
	 * @memberof MetricService
	 */
	getTxCount(network_name, channel_genesis_hash) {
		return this.sql.getRowsBySQlCase(
			`select count(1) c from transactions where channel_genesis_hash='${channel_genesis_hash}' and network_name='${network_name}' `
		);
	}

	/**
	 *
	 *
	 * @param {*} channel_genesis_hash
	 * @returns
	 * @memberof MetricService
	 */
	getBlockCount(network_name, channel_genesis_hash) {
		return this.sql.getRowsBySQlCase(
			`select count(1) c from blocks where channel_genesis_hash='${channel_genesis_hash}' and network_name='${network_name}' `
		);
	}

	/**
	 *
	 *
	 * @param {*} channel_genesis_hash
	 * @returns
	 * @memberof MetricService
	 */
	async getPeerData(network_name, channel_genesis_hash) {
		const peerArray = [];
		const c1 = await this.sql
			.getRowsBySQlNoCondition(`select channel.name as channelName,c.requests as requests,c.channel_genesis_hash as channel_genesis_hash ,
    c.server_hostname as server_hostname, c.mspid as mspid, c.peer_type as peer_type  from peer as c inner join  channel on
    c.channel_genesis_hash=channel.channel_genesis_hash where c.channel_genesis_hash='${channel_genesis_hash}' and c.network_name='${network_name}' `);
		for (let i = 0, len = c1.length; i < len; i++) {
			const item = c1[i];
			peerArray.push({
				name: item.channelName,
				requests: item.requests,
				server_hostname: item.server_hostname,
				channel_genesis_hash: item.channel_genesis_hash,
				mspid: item.mspid,
				peer_type: item.peer_type
			});
		}
		return peerArray;
	}

	/**
	 *
	 *
	 * @returns
	 * @memberof MetricService
	 */
	async getOrdererData(network_name) {
		const ordererArray = [];
		const c1 = await this.sql.getRowsBySQlNoCondition(
			`select c.requests as requests,c.server_hostname as server_hostname,c.channel_genesis_hash as channel_genesis_hash from orderer c where network_name='${network_name}' `
		);
		for (let i = 0, len = c1.length; i < len; i++) {
			const item = c1[i];
			ordererArray.push({
				requests: item.requests,
				server_hostname: item.server_hostname,
				channel_genesis_hash: item.channel_genesis_hash
			});
		}
		return ordererArray;
	}

	/**
	 *
	 *
	 * @param {*} channel_genesis_hash
	 * @returns
	 * @memberof MetricService
	 */
	async getTxPerChaincodeGenerate(network_name, channel_genesis_hash) {
		const txArray = [];
		const c = await this.sql
			.getRowsBySQlNoCondition(`select  c.name as chaincodename,channel.name as channelname ,c.version as version,c.channel_genesis_hash
       as channel_genesis_hash,c.path as path ,txcount  as c from chaincodes as c inner join channel on c.channel_genesis_hash=channel.channel_genesis_hash where  c.channel_genesis_hash='${channel_genesis_hash}' and  c.network_name='${network_name}' `);
		if (c) {
			c.forEach((item, index) => {
				logger.debug(' item ------------> ', item);
				txArray.push({
					chaincodename: item.chaincodename,
					channelName: item.channelname,
					path: item.path,
					version: item.version,
					txCount: item.c,
					channel_genesis_hash: item.channel_genesis_hash
				});
			});
		}
		return txArray;
	}

	/**
	 *
	 *
	 * @param {*} channel_genesis_hash
	 * @returns
	 * @memberof MetricService
	 */
	async getOrgsData(network_name, channel_genesis_hash) {
		const rows = await this.sql.getRowsBySQlNoCondition(
			`select mspid from peer  where channel_genesis_hash='${channel_genesis_hash}' and network_name='${network_name}'`
		);	
		return rows;
	}

	/**
	 *
	 *
	 * @param {*} channel_genesis_hash
	 * @param {*} cb
	 * @returns
	 * @memberof MetricService
	 */
	async getTxPerChaincode(network_name, channel_genesis_hash, cb) {
		try {
			const txArray = await this.getTxPerChaincodeGenerate(
				network_name,
				channel_genesis_hash
			);
			return cb(txArray);
		} catch (err) {
			logger.error(err);
			return cb([]);
		}
	}

	/**
	 *
	 *
	 * @param {*} channel_genesis_hash
	 * @returns
	 * @memberof MetricService
	 */
	async getStatusGenerate(network_name, channel_genesis_hash) {
		let chaincodeCount = await this.getChaincodeCount(
			network_name,
			channel_genesis_hash
		);
		if (!chaincodeCount) {
			chaincodeCount = 0;
		}
		let txCount = await this.getTxCount(network_name, channel_genesis_hash);
		if (!txCount) {
			txCount = 0;
		}
		txCount.c = txCount.c ? txCount.c : 0;
		let blockCount = await this.getBlockCount(network_name, channel_genesis_hash);
		if (!blockCount) {
			blockCount = 0;
		}
		blockCount.c = blockCount.c ? blockCount.c : 0;
		let peerCount = await this.getPeerlistCount(
			network_name,
			channel_genesis_hash
		);
		if (!peerCount) {
			peerCount = 0;
		}
		peerCount.c = peerCount.c ? peerCount.c : 0;
		return {
			chaincodeCount: chaincodeCount.c.toString(),
			txCount: txCount.c.toString(),
			latestBlock: blockCount.c.toString(),
			peerCount: peerCount.c.toString()
		};
	}

	/**
	 *
	 *
	 * @param {*} channel_genesis_hash
	 * @param {*} cb
	 * @returns
	 * @memberof MetricService
	 */
	async getStatus(network_name, channel_genesis_hash, cb) {
		try {
			const data = await this.getStatusGenerate(
				network_name,
				channel_genesis_hash
			);
			return cb(data);
		} catch (err) {
			logger.error(err);
			return cb([]);
		}
	}

	/**
	 *
	 *
	 * @param {*} channel_genesis_hash
	 * @param {*} cb
	 * @returns
	 * @memberof MetricService
	 */
	async getPeerList(network_name, channel_genesis_hash, cb) {
		try {
			const peerArray = await this.getPeerData(network_name, channel_genesis_hash);
			if (cb) {
				return cb(peerArray);
			}
			return peerArray;
		} catch (err) {
			logger.error(err);
			return cb([]);
		}
	}

	/**
	 *
	 *
	 * @param {*} cb
	 * @returns
	 * @memberof MetricService
	 */
	async getOrdererList(network_name, cb) {
		try {
			const ordererArray = await this.getOrdererData(network_name);
			return cb(ordererArray);
		} catch (err) {
			logger.error(err);
			return cb([]);
		}
	}

	/**
	 *
	 *
	 * @param {*} channel_genesis_hash
	 * @param {*} hours
	 * @returns
	 * @memberof MetricService
	 */
	async getTxByMinute(network_name, channel_genesis_hash, hours) {
		
		var endtime = moment().format("YYYY-MM-DD HH:mm:ss");
		var starttime = moment().subtract(hours, "hours").format("YYYY-MM-DD HH:mm:ss")
		const sqlPerMinute = `SELECT DATE_FORMAT(createdt, '%Y-%m-%d %H:%i:00') AS datetime, COUNT(createdt) AS count FROM fabricexplorer.transactions WHERE createdt BETWEEN '${starttime}' AND '${endtime}' and channel_genesis_hash = '${channel_genesis_hash}' and network_name='${network_name}' GROUP BY datetime ORDER BY datetime;`;

		var getTxByMinuteList = await this.sql.getRowsBySQlQuery(sqlPerMinute);
		var setTXMinuteList = this.getMinuteList(starttime , endtime , getTxByMinuteList);

		return setTXMinuteList;

		// return this.sql.getRowsBySQlQuery(sqlPerMinute);
	}

	/**
	 *
	 *
	 * @param {*} channel_genesis_hash
	 * @param {*} day
	 * @returns
	 * @memberof MetricService
	 */
	async getTxByHour(network_name, channel_genesis_hash, day) {

		var endtime = moment().format("YYYY-MM-DD HH:mm:ss");
		var starttime = moment().subtract(day, "days").format("YYYY-MM-DD HH:mm:ss")
		const sqlPerHour = `SELECT DATE_FORMAT(createdt, '%Y-%m-%d %H:00:00') AS datetime, COUNT(createdt) AS count FROM fabricexplorer.transactions WHERE createdt BETWEEN '${starttime}' AND '${endtime}' and channel_genesis_hash = '${channel_genesis_hash}' and network_name='${network_name}' GROUP BY datetime ORDER BY datetime;`;

		var TxHourList = await this.sql.getRowsBySQlQuery(sqlPerHour);
		var setTxByHourList = this.getHourList(starttime , endtime , TxHourList)
		return setTxByHourList;
	}

	/**
	 *
	 *
	 * @param {*} channel_genesis_hash
	 * @param {*} days
	 * @returns
	 * @memberof MetricService
	 */
	getTxByDay(network_name, channel_genesis_hash, days) {

		var endtime = moment().format("YYYY-MM-DD HH:mm:ss");
		var starttime = moment().subtract(days, "days").format("YYYY-MM-DD HH:mm:ss")
		const sqlPerHour = `SELECT DATE_FORMAT(createdt, '%Y-%m-%d %H:00:00') AS datetime, COUNT(createdt) AS count FROM fabricexplorer.transactions WHERE createdt BETWEEN '${starttime}' AND '${endtime}' and channel_genesis_hash = '${channel_genesis_hash}' and network_name='${network_name}' GROUP BY datetime ORDER BY datetime;`;

		return this.sql.getRowsBySQlQuery(sqlPerHour);
	}

	/**
	 *
	 *
	 * @param {*} channel_genesis_hash
	 * @param {*} weeks
	 * @returns
	 * @memberof MetricService
	 */
	getTxByWeek(network_name, channel_genesis_hash, weeks) {
		var endtime = moment().format("YYYY-MM-DD HH:mm:ss");
		var starttime = moment().subtract(weeks, "weeks").format("YYYY-MM-DD HH:mm:ss")
		const sqlPerWeek = ` SELECT DATE_FORMAT(createdt, '%Y-%u') AS datetime, COUNT(createdt) AS count FROM fabricexplorer.transactions WHERE createdt BETWEEN '${starttime}' AND '${endtime}' and channel_genesis_hash = '${channel_genesis_hash}' and network_name='${network_name}' GROUP BY datetime ORDER BY datetime;`;

		return this.sql.getRowsBySQlQuery(sqlPerWeek);
	}

	/**
	 *
	 *
	 * @param {*} channel_genesis_hash
	 * @param {*} months
	 * @returns
	 * @memberof MetricService
	 */
	getTxByMonth(network_name, channel_genesis_hash, months) {
		var endtime = moment().format("YYYY-MM-DD HH:mm:ss");
		var starttime = moment().subtract(months, "months").format("YYYY-MM-DD HH:mm:ss")
		const sqlPerMonth = ` SELECT DATE_FORMAT(createdt, '%Y-%m') AS datetime, COUNT(createdt) AS count FROM fabricexplorer.transactions WHERE createdt BETWEEN '${starttime}' AND '${endtime}' and channel_genesis_hash = '${channel_genesis_hash}' and network_name='${network_name}' GROUP BY datetime ORDER BY datetime;`;

		return this.sql.getRowsBySQlQuery(sqlPerMonth);
	}

	/**
	 *
	 *
	 * @param {*} channel_genesis_hash
	 * @param {*} years
	 * @returns
	 * @memberof MetricService
	 */
	getTxByYear(network_name, channel_genesis_hash, years) {
		var endtime = moment().format("YYYY-MM-DD HH:mm:ss");
		var starttime = moment().subtract(years, "years").format("YYYY-MM-DD HH:mm:ss")
		const sqlPerYear = ` SELECT DATE_FORMAT(createdt, '%Y') AS datetime, COUNT(createdt) AS count FROM fabricexplorer.transactions WHERE createdt BETWEEN '${starttime}' AND '${endtime}' and channel_genesis_hash = '${channel_genesis_hash}' and network_name='${network_name}' GROUP BY datetime ORDER BY datetime;`;

		return this.sql.getRowsBySQlQuery(sqlPerYear);
	}

	// Block metrics API
	/**
	 *
	 *
	 * @param {*} channel_genesis_hash
	 * @param {*} hours
	 * @returns
	 * @memberof MetricService
	 */
	async getBlocksByMinute(network_name, channel_genesis_hash, hours) {

		var endtime = moment().format("YYYY-MM-DD HH:mm:ss");
		var starttime = moment().subtract(hours, "hours").format("YYYY-MM-DD HH:mm:ss")
		const sqlPerMinute = `SELECT DATE_FORMAT(createdt, '%Y-%m-%d %H:%i:00') AS datetime, COUNT(createdt) AS count FROM fabricexplorer.blocks WHERE createdt BETWEEN '${starttime}' AND '${endtime}' and  channel_genesis_hash = '${channel_genesis_hash}' and network_name='${network_name}' GROUP BY datetime ORDER BY datetime;`;

		var getBlocksByMinuteList = await this.sql.getRowsBySQlQuery(sqlPerMinute);
		var setBlockMinuteList = this.getMinuteList(starttime , endtime , getBlocksByMinuteList);

		return setBlockMinuteList
	}

	getMinuteList(starttime , endtime , blockByHourList) {
		var starttime_arr = starttime.split(" ")[1].split(':');
		var endtime_arr = endtime.split(" ")[1].split(':');
		var common_start_time = starttime.split(" ")[0]
		var common_end_time = endtime.split(" ")[0]
		var arr = [];
		for (let index = starttime_arr[1]+1; index < 60; index++) {
			var settime = {}
			var block_count = '0';
			if(index < 10) {
				index = '0'+ index
			}
			var time_str = common_start_time+' '+starttime_arr[0]+':'+index+':00'
			blockByHourList.forEach(element => {
				if(element.datetime.toString() == time_str) {
					block_count = element.count.toString()
				}
			});
			settime = {
				datetime :time_str,
				count : block_count
			}
			arr.push(settime)
		}
		for (let index = 0; index <= endtime_arr[1]; index++) {
			var settime = {}
			var block_count = '0';
			if(index < 10) {
			index = '0'+ index
			}	
			var time_str = common_end_time+' '+endtime_arr[0]+':'+index+':00'

			blockByHourList.forEach(element => {
				if(element.datetime.toString() == time_str) {
					block_count = element.count.toString()
				}
			});

			settime = {
				datetime :time_str,
				count : block_count
			}
			arr.push(settime)
		}
		return arr
	}

	/**
	 *
	 *
	 * @param {*} channel_genesis_hash
	 * @param {*} days
	 * @returns
	 * @memberof MetricService
	 */
	async getBlocksByHour(network_name, channel_genesis_hash, days) {
		var endtime = moment().format("YYYY-MM-DD HH:mm:ss");
		var starttime = moment().subtract(days, "days").format("YYYY-MM-DD HH:mm:ss")
		const sqlPerHour = `SELECT DATE_FORMAT(createdt, '%Y-%m-%d %H:00:00') AS datetime, COUNT(createdt) AS count FROM fabricexplorer.blocks WHERE createdt BETWEEN '${starttime}' AND '${endtime}' and channel_genesis_hash = '${channel_genesis_hash}' and network_name='${network_name}' GROUP BY datetime ORDER BY datetime;`;
		var blockByHourList = await this.sql.getRowsBySQlQuery(sqlPerHour);
		
       	var setBlockByHourList = this.getHourList(starttime , endtime , blockByHourList)
		return setBlockByHourList;
		// return this.sql.getRowsBySQlQuery(sqlPerHour);
	}

	getHourList(starttime , endtime , blockByHourList) {
		var starttime_arr = starttime.split(" ")[1].split(':')[0];
		var common_start_time = starttime.split(" ")[0]
		var common_end_time = endtime.split(" ")[0]
		var arr = [];
		starttime_arr = starttime_arr.replace(/\b(0+)/gi,"")
		for (let index = parseInt(starttime_arr)+1; index <= 24; index++) {
			var settime = {}
			var block_count = '0';
			if(index < 10) {
				index = '0'+ index
			}
			var time_str = common_start_time+' '+index+':00:00'
			blockByHourList.forEach(element => {
				if(element.datetime.toString() == time_str) {
					block_count = element.count.toString()
				}
			});
			settime = {
				datetime :time_str,
				count : block_count
			}
			arr.push(settime)
		}
		for (let index = 0; index <= parseInt(starttime_arr); index++) {
			var settime = {}
			var block_count = '0';
			if(index < 10) {
				index = '0'+ index
			}	
			var time_str = common_end_time+' '+index+':00:00'

			blockByHourList.forEach(element => {
				if(element.datetime.toString() == time_str) {
					block_count = element.count.toString()
				}
			});

			settime = {
				datetime :time_str,
				count : block_count
			}
			arr.push(settime)
		}
		return arr
	}

	/**
	 *
	 *
	 * @param {*} channel_genesis_hash
	 * @param {*} days
	 * @returns
	 * @memberof MetricService
	 */
	getBlocksByDay(network_name, channel_genesis_hash, days) {
		var endtime = moment().format("YYYY-MM-DD HH:mm:ss");
		var starttime = moment().subtract(days, "days").format("YYYY-MM-DD HH:mm:ss")
		const sqlPerDay = `SELECT DATE_FORMAT(createdt, '%Y-%m-%d %H:00:00') AS datetime, COUNT(createdt) AS count FROM fabricexplorer.blocks WHERE createdt BETWEEN '${starttime}' AND '${endtime}' and channel_genesis_hash = '${channel_genesis_hash}' and network_name='${network_name}' GROUP BY datetime ORDER BY datetime;`;

		return this.sql.getRowsBySQlQuery(sqlPerDay);
	}

	/**
	 *
	 *
	 * @param {*} channel_genesis_hash
	 * @param {*} weeks
	 * @returns
	 * @memberof MetricService
	 */
	getBlocksByWeek(network_name, channel_genesis_hash, weeks) {
		var endtime = moment().format("YYYY-MM-DD HH:mm:ss");
		var starttime = moment().subtract(weeks, "weeks").format("YYYY-MM-DD HH:mm:ss")
		const sqlPerWeek = ` SELECT DATE_FORMAT(createdt, '%Y-%u') AS datetime, COUNT(createdt) AS count FROM fabricexplorer.blocks WHERE createdt BETWEEN '${starttime}' AND '${endtime}' and channel_genesis_hash = '${channel_genesis_hash}' and network_name='${network_name}' GROUP BY datetime ORDER BY datetime;`;

		return this.sql.getRowsBySQlQuery(sqlPerWeek);
	}

	/**
	 *
	 *
	 * @param {*} channel_genesis_hash
	 * @param {*} months
	 * @returns
	 * @memberof MetricService
	 */
	getBlocksByMonth(network_name, channel_genesis_hash, months) {
		var endtime = moment().format("YYYY-MM-DD HH:mm:ss");
		var starttime = moment().subtract(months, "months").format("YYYY-MM-DD HH:mm:ss")
		const sqlPerMonth = ` SELECT DATE_FORMAT(createdt, '%Y-%m') AS datetime, COUNT(createdt) AS count FROM fabricexplorer.blocks WHERE createdt BETWEEN '${starttime}' AND '${endtime}' and channel_genesis_hash = '${channel_genesis_hash}' and network_name='${network_name}' GROUP BY datetime ORDER BY datetime;`;

		return this.sql.getRowsBySQlQuery(sqlPerMonth);
	}

	/**
	 *
	 *
	 * @param {*} channel_genesis_hash
	 * @param {*} years
	 * @returns
	 * @memberof MetricService
	 */
	getBlocksByYear(network_name, channel_genesis_hash, years) {
		var endtime = moment().format("YYYY-MM-DD HH:mm:ss");
		var starttime = moment().subtract(years, "years").format("YYYY-MM-DD HH:mm:ss")
		const sqlPerYear = ` SELECT DATE_FORMAT(createdt, '%Y') AS datetime, COUNT(createdt) AS count FROM fabricexplorer.Transactions WHERE createdt BETWEEN '${starttime}' AND '${endtime}' and channel_genesis_hash = '${channel_genesis_hash}' and network_name='${network_name}' GROUP BY datetime ORDER BY datetime;`;

		return this.sql.getRowsBySQlQuery(sqlPerYear);
	}

	/**
	 *
	 *
	 * @param {*} channel_genesis_hash
	 * @returns
	 * @memberof MetricService
	 */
	async getTxByOrgs(network_name, channel_genesis_hash) {
		const sqlPerOrg = ` select count(creator_msp_id) as count, creator_msp_id
      from transactions
      where channel_genesis_hash ='${channel_genesis_hash}' and network_name='${network_name}'
	  group by  creator_msp_id`;
		var arr = await this.sql.getRowsBySQlQuery(sqlPerOrg);
		
		return arr;
	}

	/**
	 *
	 *
	 * @param {*} channel_genesis_hash
	 * @param {*} maxHeight
	 * @returns
	 * @memberof MetricService
	 */
	// async findMissingBlockNumber(network_name, channel_genesis_hash, maxHeight) {
	// 	const sqlQuery = `SELECT s.id AS missing_id
    // FROM generate_series(0, ${maxHeight}) s(id) WHERE NOT EXISTS (SELECT 1 FROM blocks WHERE blocknum = s.id and channel_genesis_hash ='${channel_genesis_hash}' and network_name='${network_name}' )`;

	// 	return this.sql.getRowsBySQlQuery(sqlQuery);
	// }
	async findMissingBlockNumber(network_name, channel_genesis_hash, maxHeight) {
	// 	const sqlQuery = `SELECT s.id AS missing_id
    // FROM generate_series(0, ${maxHeight}) s(id) WHERE NOT EXISTS (SELECT count(1) FROM blocks WHERE blocknum = s.id and channel_genesis_hash ='${channel_genesis_hash}' and network_name='${network_name}' )`;
		const sqlQuery = `SELECT * FROM blocks WHERE channel_genesis_hash ='${channel_genesis_hash}' and network_name='${network_name}'`;

		// return this.sql.getRowsBySQlQuery(sqlQuery);

		var res = await this.sql.getRowsBySQlQuery(sqlQuery);
		return  this.set_sql_generate_series(res , maxHeight)
	}

	set_sql_generate_series(res , maxHeight){
		var blockarr = [];
		var miss = '';
		var isset_block = [];
		
		if(res !== null) {
			res.forEach((a) => {
				isset_block.push(a.blocknum)
			})
			for (let i = 0; i < maxHeight; i++) {
				if(isset_block.includes(i) == false) {
					miss = {'missing_id': i};
					blockarr.push(miss)
				}
				
			}
		}
		
		return blockarr

	}
}

module.exports = MetricService;
